package cn.hallele.bbts.core.module.database.converter;

import cn.hallele.bbts.core.common.constants.MsgCodeConst;
import cn.hallele.bbts.core.common.exceptions.CoreException;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * json bean 数据库json类型与实体映射转换器
 * jpa框架需要继承，mybatis直接使用即可
 *
 * @author anle5
 * @since 2024/1/16 星期二 23:57
 */
@Slf4j
public class JBConverter<T> extends BaseTypeHandler<T> implements AttributeConverter<T, String> {
    private static final ObjectMapper MAPPER = SpringUtil.getBean(ObjectMapper.class);

    /**
     * 映射类
     */
    private final Class<T> CLASS;

    public JBConverter(Class<T> clazz) {
        if (clazz == null) {
            log.error("Type argument [clazz] can not be null.");
            throw new CoreException(MsgCodeConst.DATA_PARSE_FAILED, "Type argument [clazz] can not be null");
        }
        this.CLASS = clazz;
    }

    /**
     * bean 转json，jpa使用
     *
     * @param t t
     * @return String
     */
    @Override
    public String convertToDatabaseColumn(T t) {
        return this.toJson(t);
    }

    /**
     * json 转bean，jpa使用
     *
     * @param res E
     * @return JsonNode
     */
    @Override
    public T convertToEntityAttribute(String res) {
        return this.toObject(res);
    }

    /**
     * mybatis 使用
     *
     * @param ps        PreparedStatement
     * @param i         i
     * @param parameter parameter
     * @param jdbcType  jdbcType
     * @throws SQLException e
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, this.toJson(parameter));
    }

    /**
     * mybatis 使用
     *
     * @param rs         rs
     * @param columnName columnName
     * @return 泛型，对应参数的bean
     * @throws SQLException e
     */
    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.toObject(rs.getString(columnName));
    }

    /**
     * mybatis 使用
     *
     * @param rs          rs
     * @param columnIndex columnIndex
     * @return 泛型，对应参数的bean
     * @throws SQLException e
     */
    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.toObject(rs.getString(columnIndex));
    }

    /**
     * mybatis 使用
     *
     * @param cs          cs
     * @param columnIndex columnIndex
     * @return 泛型，对应参数的bean
     * @throws SQLException e
     */
    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.toObject(cs.getString(columnIndex));
    }

    /**
     * mybatis 使用
     *
     * @param object 泛型，对应参数的bean
     * @return string
     */
    private String toJson(T object) {
        try {
            return MAPPER.writeValueAsString(object);
        } catch (Exception e) {
            log.error("Data parse failed, reason: {}.", e.getMessage(), e);
            throw new CoreException(MsgCodeConst.DATA_PARSE_FAILED, e.getMessage());
        }
    }

    /**
     * mybatis 使用
     *
     * @param content content
     * @return 泛型，对应参数的bean
     */
    private T toObject(String content) {
        // 不能忽略空值，否则不好定位
        if (content == null) {
            return null;
        }

        try {
            return MAPPER.readValue(content, this.CLASS);
        } catch (Exception e) {
            log.error("Data parse failed, reason: {}.", e.getMessage(), e);
            throw new CoreException(MsgCodeConst.DATA_PARSE_FAILED, e.getMessage());
        }
    }
}
